[READ-ONLY] Mirror of https://github.com/flo-bit/skywatched. review movies and tv shows, based on at proto skywatched.app
0

Configure Feed

Select the types of activity you want to include in your feed.

skywatched / src / routes / [kind] / [id] / +page.svelte
5.5 kB 166 lines
1<script lang="ts"> 2 import { type PageData } from './$types'; 3 import { cn } from '$lib/utils'; 4 import { rateMovieModal, videoPlayer, watchedItems } from '$lib/state.svelte'; 5 6 import Container from '$lib/Components/Container.svelte'; 7 import ItemsList from '$lib/Components/ItemsList.svelte'; 8 import ReviewList from '$lib/Components/ReviewList.svelte'; 9 import Avatar from '$lib/Components/Avatar.svelte'; 10 11 let { data }: { data: PageData } = $props(); 12</script> 13 14<svelte:head> 15 <title>{data.result.title ?? data.result.name ?? ''} | skywatched</title> 16</svelte:head> 17 18{#snippet buttons()} 19 {#if data.user && !watchedItems.hasRated(data.result)} 20 <button 21 class={cn( 22 'inline-flex items-center gap-2 rounded-md bg-white/10 px-3.5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all duration-75 hover:bg-white/20 ', 23 watchedItems.hasRated(data.result) 24 ? 'bg-green-500/10 text-green-400 hover:bg-green-500/20' 25 : '' 26 )} 27 onclick={() => { 28 rateMovieModal.show({ 29 movieId: data.result.movieId, 30 showId: data.result.showId, 31 kind: data.result.movieId ? 'movie' : 'tv', 32 name: data.result.original_title ?? data.result.original_name, 33 posterPath: data.result.poster_path, 34 currentRating: watchedItems.getRating(data.result)?.rating ?? 0, 35 currentReview: watchedItems.getRating(data.result)?.ratingText ?? '' 36 }); 37 }} 38 > 39 rate {data.kind === 'movie' ? 'movie' : 'show'} 40 </button> 41 {/if} 42 {#if data.trailer} 43 <button 44 onclick={() => videoPlayer.show(data.trailer ?? '')} 45 type="button" 46 class="inline-flex w-fit items-center gap-x-1.5 rounded-md border border-accent-500/30 bg-accent-700/20 px-3 py-2 text-sm font-semibold text-accent-400 shadow-sm transition-all duration-100 hover:bg-accent-700/30 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent-600" 47 > 48 <svg 49 xmlns="http://www.w3.org/2000/svg" 50 viewBox="0 0 24 24" 51 fill="currentColor" 52 class="-ml-0.5 size-5" 53 > 54 <path 55 d="M4.5 4.5a3 3 0 0 0-3 3v9a3 3 0 0 0 3 3h8.25a3 3 0 0 0 3-3v-9a3 3 0 0 0-3-3H4.5ZM19.94 18.75l-2.69-2.69V7.94l2.69-2.69c.944-.945 2.56-.276 2.56 1.06v11.38c0 1.336-1.616 2.005-2.56 1.06Z" 56 /> 57 </svg> 58 59 Trailer 60 </button> 61 {/if} 62{/snippet} 63 64<img 65 src="https://image.tmdb.org/t/p/w780{data.result.backdrop_path}" 66 alt="" 67 class="fixed h-full w-full object-cover object-center opacity-20" 68/> 69<div class="fixed inset-0 h-full w-full bg-black/50"></div> 70 71<Container class="relative z-10 pb-8 pt-4"> 72 <div class="flex gap-4 px-4 pt-8"> 73 <img 74 src="https://image.tmdb.org/t/p/w500{data.result.poster_path}" 75 alt="" 76 class="poster h-36 w-24 shrink-0 rounded-lg border border-white/10 sm:h-64 sm:w-44" 77 style:--name={`poster-${data.result.id}`} 78 /> 79 <div class="flex flex-col gap-4"> 80 <div 81 class="title max-w-xl text-2xl font-semibold text-white sm:text-4xl" 82 style:--name={`title-${data.result.id}`} 83 > 84 {data.result.title ?? data.result.name} 85 </div> 86 87 {#if data.settings?.streaming_region?.code && data.watchProviders[data.settings.streaming_region.code]?.flatrate} 88 <div class="mt-2 text-sm text-white sm:mt-4"> 89 <div class="mb-2 flex flex-wrap gap-4 text-xs font-medium"> 90 stream on 91 <span class="flex items-center gap-2 text-base-400" 92 >from <a 93 href="https://www.justwatch.com" 94 target="_blank" 95 class="text-base-300 hover:text-accent-300" 96 > 97 <img src="/justwatch_logo.svg" alt="justwatch" class="h-3" /> 98 </a></span 99 > 100 </div> 101 <a 102 href={data.watchProviders[data.settings.streaming_region.code].link} 103 target="_blank" 104 class="flex flex-wrap gap-2" 105 > 106 {#each data.watchProviders[data.settings.streaming_region.code].flatrate as provider} 107 <img 108 src="https://image.tmdb.org/t/p/w500{provider.logo_path}" 109 alt={provider.provider_name} 110 class="size-8 rounded-md border border-base-800 md:size-12" 111 /> 112 {/each} 113 </a> 114 </div> 115 {/if} 116 <div class="hidden gap-2 sm:flex"> 117 {@render buttons()} 118 </div> 119 </div> 120 </div> 121 122 <div class="px-4 pt-4 text-sm text-white"> 123 <div class="mb-4 flex gap-2 sm:hidden"> 124 {@render buttons()} 125 </div> 126 <div class="mb-4 max-w-2xl text-sm text-white"> 127 <div class="mb-2 text-lg font-semibold">overview</div> 128 {data.result.overview} 129 </div> 130 </div> 131 132 {#if data.ratings.length > 0} 133 <div class="mt-8 px-4 text-lg font-semibold">reviews</div> 134 135 <ReviewList reviews={data.ratings} showMovieDetails={false} class="" /> 136 {/if} 137 138 {#if data.recommendations.length > 0} 139 <div class="px-4 pt-4 text-sm text-white"> 140 <div class="mb-2 text-lg font-semibold">recommendations</div> 141 142 <ItemsList items={data.recommendations} showMark={!!data.user} /> 143 </div> 144 {/if} 145 146 {#if data.cast.length > 0} 147 <div class="px-4 pb-8 pt-4 text-sm text-white"> 148 <div class="mb-2 text-lg font-semibold">cast</div> 149 150 <div class={cn('flex gap-x-6 overflow-x-auto')}> 151 {#each data.cast as castMember} 152 <a href={`/cast/${castMember.id}`} class="flex flex-col items-center gap-1"> 153 <Avatar 154 src={castMember.profile_path 155 ? 'https://image.tmdb.org/t/p/w500' + castMember.profile_path 156 : undefined} 157 size="size-32" 158 /> 159 <div class="text-center text-xs font-medium">{castMember.name}</div> 160 <div class="text-center text-xs text-base-400">{castMember.character}</div> 161 </a> 162 {/each} 163 </div> 164 </div> 165 {/if} 166</Container>